#ifndef __EPOLL_SERVER_HPP__
#define __EPOLL_SERVER_HPP_

#include <iostream>
#include <functional>
#include <string>
#include <cstdio>
#include <cassert>
#include <unistd.h>
#include "Sock.hpp"
#include "Epoll.hpp"

using NS_sock::Sock;
using NS_epoll::Epoll;

class EpollServer
{
    using func_t = std::function<void(std::string)>;
private:
    uint16_t _port;
    int _listenSock;
    int _epfd;
    struct epoll_event *_revs;
    int _evnum;

     func_t _handlerRequest;
public:
    EpollServer(const int &port,func_t handlerRequest,int evnum=99)
        :_port(port)
        ,_evnum(evnum)
        ,_handlerRequest(handlerRequest)
    {
        // 1.套接字创建,绑定,监听
        _listenSock=Sock::Socket();
        Sock::Bind(_listenSock,_port);
        Sock::Listen(_listenSock);
        // 2.创建epoll模型
        _epfd=Epoll::CreateEpoll();
        if(_epfd<0) exit(7);
        // 3.将listenSock添加到epoll模型中(即插入到红黑树中,并注册相应的回调)
        if(!Epoll::CtlEpoll(_epfd,EPOLL_CTL_ADD,_listenSock,EPOLLIN)) exit(8);
        // 4.申请struct epoll_event数组,用来获取就绪队列中的fd
        // _revs=reinterpret_cast<struct epoll_event*>(malloc( _evnum * sizeof(struct epoll_event) ));
        _revs=new struct epoll_event[_evnum];
    }

    void Accepter()
    {
        // 获取客服端对应的sockfd
        uint16_t clientport;
        std::string clientip;
        int sockfd=Sock::Accept(_listenSock,&clientport,&clientip);

        // 将sockfd加入到epoll模型中
        Epoll::CtlEpoll(_epfd,EPOLL_CTL_ADD,sockfd,EPOLLIN);

        printf("[%s %d]# add sockfd:%d success\n",clientip.c_str(),clientport,sockfd);
    }

    void Recver(int sockfd)
    {
        char buffer[1024];
        int ret=recv(sockfd,buffer,sizeof(buffer)-1,0);
        if(ret>0)
        {
            buffer[ret]='\0';
            std::cout<<"sockfd: "<<sockfd<<"# "<<buffer<<std::endl;
        }
        else if(0==ret)
        {
            // 客服端调用了close
            std::cout<<"client close , I close too"<<std::endl;
            Epoll::CtlEpoll(_epfd,EPOLL_CTL_DEL,sockfd,0);
            close(sockfd);
        }
        else
        {
            // 读取失败
            perror("recv error");
            Epoll::CtlEpoll(_epfd,EPOLL_CTL_DEL,sockfd,0);
            close(sockfd);
            // std::cerr<<"recv error"<<std::endl;
        }
    }

    void Process(int n)
    {
        assert(n>0);
        // 根据epoll_wait的返回值遍历_revs
        for(int i=0;i<n;++i)
        {
            uint32_t revents = _revs[i].events;
            int sock = _revs[i].data.fd;
            if(_revs[i].events&EPOLLIN)
            {
                if(_revs[i].data.fd==_listenSock)
                {
                    Accepter();
                }
                else
                {
                    Recver(_revs->data.fd);
                }
            }
            if(_revs[i].events&EPOLLOUT)
            {
                //...
            } 
        }
    }

    void LoopOnce(int timeout)
    {
        int n=Epoll::WaitEpoll(_epfd,_revs,_evnum,timeout);
        // if(n==_evnum) // 扩容
        switch(n)
        {
            case -1:
                perror("poll error");
                break;
            case 0:
                std::cout<<"No file descriptor is ready"<<std::endl;
                break;
            default:
                std::cout<<"The file descriptor is ready , num: "<<n<<std::endl;
                Process(n);
                break;
        }
    }

    void Start()
    {
        int timeout=1000;
        while(true)
        {
            LoopOnce(timeout);
        }
    }

    ~EpollServer()
    {
        if(_listenSock>=0)
        {
            close(_listenSock);
        }
        if(_epfd>=0)
        {
            close(_epfd);
        }
        if(_revs!=nullptr)
        {
            delete [] _revs;
        }
    }
};

#endif